/**
 * This file is released under the GNU General Public License.
 * Refer to the COPYING file distributed with this package.
 *
 * Copyright (c) 2008-2009 WURFL-Pro srl
 */
package net.sourceforge.wurfl.core;

import net.sourceforge.wurfl.core.cache.CacheProvider;
import net.sourceforge.wurfl.core.cache.LRUMapCacheProvider;
import net.sourceforge.wurfl.core.handlers.matchers.MatcherManager;
import net.sourceforge.wurfl.core.request.WURFLRequest;

import org.apache.commons.lang.Validate;
import org.apache.commons.lang.text.StrBuilder;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * <p>
 * This is the default implementation of WURFLService. It is responsible to
 * match the request user-agent with a managed device's identifier and returns a
 * Device instance.
 * </p>
 * <p>
 * MatcherManager instance is used to match WURFLRequest with one ModelDevice
 * identifier.
 * </p>
 * <p>
 * It uses a CacheProvider to cache Devices instance by the WURFLRequests.
 * </p>
 * 
 * @author WURFL-PRO SRL, Rome, Italy
 * @version $Id: DefaultWURFLService.java 1045 2009-03-09 15:52:53Z filippo.deluca $
 */
public class DefaultWURFLService implements WURFLService {

	/** Log */
	private static final Log log = LogFactory.getLog(DefaultWURFLService.class);

	/** Caches device build from WURFLRequest */
	private CacheProvider cacheProvider;

	/** Matches the WURFLRequest to obtain device identifier */
	private MatcherManager matcherManager;

	/** Builds devices */
	private DeviceProvider deviceProvider;
	
	// Constructors *******************************************************

	/**
	 * Build DefaultWURFLService from their private members.
	 * 
	 * @param matcherManager
	 *            The MatcherManager instance used to match the WURFLRequest to
	 *            handled user-agent.
	 * @param deviceProvider
	 *            The DeviceProvider instance used to create Device instances.
	 * @param cacheProvider
	 *            The CacheProvider instance used to cache created Devices.
	 */
	public DefaultWURFLService(MatcherManager matcherManager,
			DeviceProvider deviceProvider, CacheProvider cacheProvider) {

		this.matcherManager = matcherManager;
		this.deviceProvider = deviceProvider;
		this.cacheProvider = cacheProvider;

		log.info("DefaultWURFLService created");
	}

	/**
	 * Build DefaultWURFLService from their private members using
	 * {@link LRUMapCacheProvider} by default.
	 * 
	 * @param matcherManager
	 *            The MatcherManager instance used to match the WURFLRequest to
	 *            handled user-agent.
	 * @param deviceProvider
	 *            The DeviceProvider instance used to create Device instances.
	 */
	public DefaultWURFLService(MatcherManager matcherManager,
			DeviceProvider deviceProvider) {

		this(matcherManager, deviceProvider, new LRUMapCacheProvider());
	}
	
	// Business methods ***************************************************

	/**
	 * {@inheritDoc}
	 */
	public Device getDeviceForRequest(WURFLRequest request) {

		// Check input
		Validate.notNull(request, "The request is null");

		// Get device from cache
		Object deviceKey = createDeviceKey(request);
		Device device = (Device) cacheProvider.get(deviceKey);

		if (log.isDebugEnabled() && device != null) {
			log.debug("Found cached device: " + device);
		}

		if (device == null) {

			if (log.isDebugEnabled()) {
				log.debug("Not found device cached with key: " + deviceKey
						+ ", matching...");
			}

			// Matching device
			String deviceId = matcherManager.matchRequest(request);
			device = deviceProvider.getDevice(deviceId);

			cacheProvider.put(deviceKey, device);

		} // if (device == null)

		assert device != null;

		return device;
	}

	// Support methods ****************************************************

	/**
	 * Create a key to store device identifier in cache. Actually it return the
	 * user-agent followed by uaprof.
	 * 
	 * <p>
	 * The String is a good choice for key because they cache the hashCode.
	 * </p>
	 * 
	 * @param request
	 *            The request to create key from.
	 * @return A Object used ad key to cache a Device by the corresponding
	 *         WURFLRequest.
	 */
	protected Object createDeviceKey(WURFLRequest request) {

		// Check preconditions
		assert request != null : "The request is null";

		/*
		 * Maybe useful to generate String or not? String are hashCode cached.
		 * so more fast
		 */
		return new StrBuilder(request.getUserAgent()).append(
				request.getUserAgentProfile()).toString();
	}

}
